package com.zkt.subject.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSON;
import com.zkt.subject.common.constants.SubjectConstant;
import com.zkt.subject.common.enums.IsDeletedFlagEnum;
import com.zkt.subject.domain.convert.SubjectCategoryConverter;
import com.zkt.subject.domain.entity.SubjectCategoryBO;
import com.zkt.subject.domain.entity.SubjectLabelBO;
import com.zkt.subject.domain.service.SubjectCategoryDomainService;
import com.zkt.subject.domain.util.CacheUtil;
import com.zkt.subject.infra.basic.entity.SubjectCategory;
import com.zkt.subject.infra.basic.entity.SubjectLabel;
import com.zkt.subject.infra.basic.entity.SubjectMapping;
import com.zkt.subject.infra.basic.service.ISubjectCategoryService;
import com.zkt.subject.infra.basic.service.ISubjectLabelService;
import com.zkt.subject.infra.basic.service.ISubjectMappingService;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author 赵开泰
 * @program jc-club
 * @date 2024/9/30
 * @description 题目类型领域服务
 **/

@Service
@Slf4j
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {
	
	@Resource
	private ISubjectCategoryService subjectCategoryService;
	
	@Resource
	private ISubjectMappingService subjectMappingService;
	
	@Resource
	private ISubjectLabelService subjectLabelService;
	
	@Resource
	private ThreadPoolExecutor labelThreadPool;
	
	@Resource
	private CacheUtil cacheUtil;
	
	/**
	 * 新增分类
	 *
	 * @param subjectCategoryBO 新增的分类数据
	 */
	@Override
	public Boolean add(SubjectCategoryBO subjectCategoryBO) {
		if (log.isInfoEnabled()) {
			log.info("SubjectCategoryDomainServiceImpl.add.bo:{}", JSON.toJSONString(subjectCategoryBO));
		}
		SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
		return subjectCategoryService.save(subjectCategory);
	}
	
	/**
	 * 查询题目分类
	 *
	 * @param subjectCategoryBO 查询条件
	 * @return 题目分类列表
	 */
	@Override
	public List<SubjectCategoryBO> queryCategory(SubjectCategoryBO subjectCategoryBO) {
		SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
		List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);
		List<SubjectCategoryBO> subjectCategoryBOList = SubjectCategoryConverter.INSTANCE.convertCategoryToBo(subjectCategoryList);
		if (log.isInfoEnabled()) {
			log.info("SubjectCategoryDomainServiceImpl.queryPrimaryCategory.boList:{}",
					JSON.toJSONString(subjectCategoryBOList));
		}
		subjectCategoryBOList.forEach(bo -> {
			long count = subjectMappingService.lambdaQuery()
					.eq(SubjectMapping::getCategoryId, bo.getId())
					.list()
					.stream()
					.map(SubjectMapping::getSubjectId)
					.distinct()
					.count();
			bo.setCount(Math.toIntExact(count));
		});
		return subjectCategoryBOList;
	}
	
	/**
	 * 更新分类
	 *
	 * @param subjectCategoryBO 需要更新的数据
	 * @return 成功与否
	 */
	@Override
	public Boolean update(SubjectCategoryBO subjectCategoryBO) {
		SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE
				.convertBoToCategory(subjectCategoryBO);
		return subjectCategoryService.updateById(subjectCategory);
	}
	
	/**
	 * 删除分类
	 *
	 * @param subjectCategoryBO 删除分类条件（id）
	 * @return 成功与否
	 */
	@Override
	public Boolean delete(SubjectCategoryBO subjectCategoryBO) {
		SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE
				.convertBoToCategory(subjectCategoryBO);
		return subjectCategoryService.removeById(subjectCategory.getId());
	}
	
	/**
	 * 一次性查询分类及标签
	 *
	 * @param subjectCategoryBO 查询条件
	 * @return 分类列表
	 */
	@Override
	@SneakyThrows
	public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBO) {
		Long id = subjectCategoryBO.getId();
		String cacheKey = SubjectConstant.CACHE_SUBJECT_QUERYCATEGORYANDLABEL + id;
		return cacheUtil.getResult(cacheKey, SubjectCategoryBO.class, key -> getSubjectCategoryBos(id));
	}
	
	private List<SubjectCategoryBO> getSubjectCategoryBos(Long categoryId) {
		SubjectCategory subjectCategory = new SubjectCategory();
		subjectCategory.setParentId(categoryId);
		subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
		
		List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);
		if (log.isInfoEnabled()) {
			log.info("SubjectCategoryController.queryCategoryAndLabel.subjectCategoryList:{}",
					JSON.toJSONString(subjectCategoryList));
		}
		
		List<SubjectCategoryBO> categoryBOList = SubjectCategoryConverter.INSTANCE.convertListBoToCategory(subjectCategoryList);
		Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
		
		List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = categoryBOList.stream()
				.map(category -> CompletableFuture.supplyAsync(
						() -> getLabelBOList(category),
						labelThreadPool)
				).toList();
		
		completableFutureList.forEach(future -> {
			try {
				Map<Long, List<SubjectLabelBO>> resultMap = future.get();
				if (!CollUtil.isEmpty(resultMap)) {
					map.putAll(resultMap);
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		});
		
		categoryBOList.forEach(categoryBO -> {
			if (!CollUtil.isEmpty(map.get(categoryBO.getId()))) {
				categoryBO.setLabelBOList(map.get(categoryBO.getId()));
			}
		});
		
		return categoryBOList;
	}
	
	private Map<Long, List<SubjectLabelBO>> getLabelBOList(SubjectCategoryBO category) {
		if (log.isInfoEnabled()) {
			log.info("getLabelBOList:{}", JSON.toJSONString(category));
		}
		
		Map<Long, List<SubjectLabelBO>> labelMap = new HashMap<>();
		List<SubjectLabel> labelList = subjectLabelService.lambdaQuery()
				.eq(SubjectLabel::getCategoryId, category.getId())
				.list();
		if (CollUtil.isEmpty(labelList)) {
			return null;
		}
		
		List<SubjectLabelBO> labelBOList = new LinkedList<>();
		labelList.forEach(label -> {
			SubjectLabelBO subjectLabelBO = new SubjectLabelBO();
			subjectLabelBO.setId(label.getId());
			subjectLabelBO.setLabelName(label.getLabelName());
			subjectLabelBO.setCategoryId(label.getCategoryId());
			subjectLabelBO.setSortNum(label.getSortNum());
			labelBOList.add(subjectLabelBO);
		});
		
		labelMap.put(category.getId(), labelBOList);
		
		return labelMap;
	}
	
}